package de.rj.urwalking.util;

import java.util.HashMap;

import com.google.android.gms.maps.model.LatLng;
import com.threed.jpct.SimpleVector;

import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.provider.Settings;
import de.rj.urwalking.config.Config;

/**
 * Contains methods that deal with the device's location.
 * 
 * @author rjj
 * 
 */

public class LocationUtil {

	/**
	 * Checks if a new location is further away from the currently used location than a predefined threshold.
	 * 
	 * @param currentBestLocation
	 * @param lastLoadedLocation
	 */
	public static boolean hasMovedSignificantly(Location currentBestLocation, Location lastLoadedLocation) {

		if (lastLoadedLocation == null) {
			return true;
		}

		if (currentBestLocation.distanceTo(lastLoadedLocation) > Config.LOCATION_THRESHOLD) {
			return true;
		}
		return false;
	}
	
	

	/**
	 * Checks if the user has manually navigated further away from the last location than a predefined threshold.
	 * 
	 */
	public static boolean hasMovedSignificantly(SimpleVector cameraPosition, SimpleVector lastManualLoadLocation) {

		SimpleVector camPosOnGround = SimpleVector.create(cameraPosition.x, cameraPosition.y, 0);

		if (camPosOnGround.distance(lastManualLoadLocation) > Config.MANUAL_LOCATION_THRESHOLD) {
			return true;
		}

		return false;
	}

	/**
	 * Determines whether one Location reading is better than the current Location fix
	 * 
	 * @param location
	 *            The new Location that you want to evaluate
	 * @param currentBestLocation
	 *            The current Location fix, to which you want to compare the new one
	 */
	public static boolean isBetterLocation(Location location, Location currentBestLocation) {
		if (currentBestLocation == null) {
			// A new location is always better than no location
			return true;
		}

		// Check whether the new location fix is newer or older
		final int TWO_MINUTES = 1000 * 60 * 2;
		long timeDelta = location.getTime() - currentBestLocation.getTime();
		boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
		boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
		boolean isNewer = timeDelta > 0;

		// If it's been more than two minutes since the current location, use
		// the new location
		// because the user has likely moved
		if (isSignificantlyNewer) {
			return true;
			// If the new location is more than two minutes older, it must be
			// worse
		} else if (isSignificantlyOlder) {
			return false;
		}

		// Check whether the new location fix is more or less accurate
		int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
		boolean isLessAccurate = accuracyDelta > 0;
		boolean isMoreAccurate = accuracyDelta < 0;
		boolean isSignificantlyLessAccurate = accuracyDelta > 200;

		// Check if the old and new location are from the same provider
		boolean isFromSameProvider = isSameProvider(location.getProvider(), currentBestLocation.getProvider());

		// Determine location quality using a combination of timeliness and
		// accuracy
		if (isMoreAccurate) {
			return true;
		} else if (isNewer && !isLessAccurate) {
			return true;
		} else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
			return true;
		}
		return false;
	}

	/**
	 * Checks whether two providers are the same.
	 */
	private static boolean isSameProvider(String provider1, String provider2) {
		if (provider1 == null) {
			return provider2 == null;
		}
		return provider1.equals(provider2);
	}

	/**
	 * Checks if mock locations are enabled.
	 */

	public static boolean areMockLocationsEnabled(Context context) {

		if (Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION).equals("0")) {
			return false;
		} else {
			return true;
		}
	}

	/**
	 * Returns a {@link Location} object for one of a few predefined test locations. 
	 * @see LocationUtil#getCoordinatesFor(String)
	 * @param locationName a String which identifies the location. Has to be defined in /res/values/strings.xml 
	 * @return
	 */
	
	public static Location getLocationFor(String locationName) {

		Double[] coordinates = LocationUtil.getCoordinatesFor(locationName);

		Location location = new Location(LocationManager.GPS_PROVIDER);
		location.setLatitude(coordinates[0]);
		location.setLongitude(coordinates[1]);
		location.setTime(System.currentTimeMillis());
		location.setAccuracy(50);

		return location;
	}

	private static Double[] getCoordinatesFor(String location) {
		HashMap<String, Double[]> coordinatesMap = new HashMap<String, Double[]>();
		// Keep in sync with array in res/values/strings.xml!
		coordinatesMap.put("Ostengasse", new Double[] { 49.01890, 12.10466 });
		coordinatesMap.put("Uni", new Double[] { 48.998071, 12.09511 });
		coordinatesMap.put("Domplatz", new Double[] { 49.019131, 12.098828 });
		coordinatesMap.put("Haidplatz", new Double[] { 49.019731, 12.093155 });
		coordinatesMap.put("Bismarckplatz", new Double[] { 49.018705, 12.089627 });

		return coordinatesMap.get(location);
	}

}
